/*
 * Created by SharpDevelop.
 * User: MyWife
 * Date: 13/09/08
 * Time: 8:30 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Mail;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using popLib.ExtensionClass;
using System.Text;


namespace popLib
{
	/// <summary>
	/// Description of NetLib.
	/// </summary>
	public class NetLib
	{
		public static string sendMail(string from, string to, string subject, string body, string server, int port, string username, string password){
			return sendMail(from, to, null, null, subject, body, null,server, port, username, password, false);
		}
		
		public static string sendMail (string from, string to, string cc, string bcc, string subject, string body, string[] fileAttack, string server, int port, string username, string password, bool enableSsl) {
			// Mail initialization
			MailMessage mailMsg = new MailMessage();
			
			mailMsg.From = new MailAddress(from);
			if(StringLib.isValid(to)) foreach( string mail in StringLib.splitByAny(to, ";,") ) mailMsg.To.Add(mail);
			else return "Destination is not valid!";
			
			if(StringLib.isValid(cc)) foreach( string mail in StringLib.splitByAny(cc, ";,") ) mailMsg.CC.Add(mail);
			if(StringLib.isValid(bcc)) foreach( string mail in StringLib.splitByAny(bcc, ";,") ) mailMsg.Bcc.Add(mail);
			
			mailMsg.Subject 	 = subject;
			mailMsg.BodyEncoding = System.Text.Encoding.Unicode;
			mailMsg.Body 		 = body;
			mailMsg.Priority 	 = MailPriority.High;
			mailMsg.BodyEncoding = System.Text.Encoding.UTF8;
			
			if(fileAttack != null && fileAttack.Length > 0)
				foreach( string tmpFile in fileAttack )
				if( tmpFile.Trim().Length > 0 && File.Exists(tmpFile) )
				mailMsg.Attachments.Add( new Attachment(tmpFile) );
			
			
			SmtpClient smtp = new SmtpClient(server, port);
			if( StringLib.isValid(username) && password != null ){
				System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(username, password);
				smtp.Credentials = credentials;
			}
			
			smtp.EnableSsl = enableSsl;
			
			try
			{
				smtp.Send(mailMsg);
				return "";
			}
			catch (Exception ex)
			{
				return ex.Message;
			}
		}
		
		public static ReturnSet DownloadFile(string url, string localFile, NetworkSecurity netsec, int retryTimes){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = DownloadFile( url, localFile, netsec );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}
		
		public static ReturnSet DownloadFile(string url, string localFile, NetworkSecurity netsec){
			try{
				WebClient client = new WebClient();
				
				if( netsec != null && netsec.Proxy != null ){
					client.Proxy = netsec.Proxy;
				}
				
				client.DownloadFile( url, localFile );
				return new ReturnSet( true, localFile );
			}catch(Exception ex){
				return new ReturnSet( false,
				                     string.Format( "URL: {0}\r\nLocalFile: {1}\r\n{2}", url, localFile, ex.ToString()));
			}
		}
		
		public static ReturnSet UploadFile(string localFile, string url ){
			return UploadFile( url, localFile, null, null );
		}
		
		public static ReturnSet UploadFile(string localFile, string url, Hashtable paramList ){
			return UploadFile( url, localFile, paramList, null );
		}
		
		public static ReturnSet UploadFile(string localFile, string url, Hashtable paramList, NetworkSecurity netsec ){
			try{
				WebClient client = new WebClient();
				
				if( netsec != null && netsec.Proxy != null ){
					client.Proxy = netsec.Proxy;
				}
								
				if( paramList != null ){
					foreach( string key in paramList.Keys )
						client.Headers.Add( key, paramList[key].ToString() );
				}
				byte[] result = client.UploadFile( url, localFile );
				
				return new ReturnSet( true, System.Text.Encoding.UTF32.GetString(result) );
			}catch(Exception ex){
				return new ReturnSet( false, ex, string.Format( "URL: {0}\r\nLocalFile: {1}\r\n{2}", url, localFile, ex.ToString()));
			}
		}
		
		public static ReturnSet UploadFile(string localFile, string url, Hashtable paramList, NetworkSecurity netsec, int retryTimes  ){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = UploadFile( localFile, url, paramList, netsec );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}
		
		
		#region ReturnSet counterTry(...)
		
		public static ReturnSet counterTry( DelegateSet.counterTryDelegate2 func, int retryTimes, string param1, string param2 ){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = func( param1, param2 );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}
		
		public static ReturnSet counterTry( DelegateSet.counterTryDelegate3 func, int retryTimes, string param1, string param2, string param3 ){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = func( param1, param2, param3 );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}
		
		
		public static ReturnSet counterTry( DelegateSet.counterTryDelegate4 func, int retryTimes, string param1, string param2, string param3, string param4 ){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = func( param1, param2, param3, param4 );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}
		
		
		public static ReturnSet counterTry( DelegateSet.counterTryDelegate5 func, int retryTimes, string param1, string param2, string param3, string param4, string param5 ){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = func( param1, param2, param3, param4, param5 );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}
		
		
		#endregion
		
		
		public static ReturnSet createImapServer(string host, string username, string password, bool useSSL){
			string[] parts = host.Split(':');
			int port = (parts.Length == 2) ? int.Parse(parts[1]) : useSSL ? 993 : 143 ;
			ExtensionClass.Imap imap = new ExtensionClass.Imap(parts[0], port, useSSL);
			return new ReturnSet( imap.AuthenticateUser( username, password ), imap );
		}
		
		
		/************************ HTTP REQUEST ******************************/
		static CookieCollection Cookies;
		
		#region basic https functions
		private static ReturnSet InitWebRequest( string url, NetworkSecurity netsec ){
			ReturnSet result = new ReturnSet( false, null );
			try{
				
				HttpWebRequest request 					= (HttpWebRequest)System.Net.WebRequest.Create(url);
				request.Method 							= "POST";
				request.KeepAlive 						= true;
				if (Cookies != null && Cookies.Count > 0) request.CookieContainer.Add(Cookies);
				//request.UserAgent 						= "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)";
				
				if( netsec!= null && netsec.Certificate != null )
				{
					request.ClientCertificates.Add(netsec.Certificate);
					ServicePointManager.ServerCertificateValidationCallback += delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
					{
						return true;
					};
				}

				if ( netsec!= null && netsec.Proxy != null )
				{
					request.Proxy = netsec.Proxy;
				}
				
				result = new ReturnSet( true, request );
			}catch (WebException ex){
				result = new ReturnSet( false, ex, "ERROR_MESSAGE=HttpWebRequest error, can not connect to server ("+url+")\r\n\r\n" + ex.ToString() );
			}
			return result;
		}
		
		private static ReturnSet GetResponseStr(HttpWebRequest request){
			ReturnSet result = new ReturnSet( false, null );
			try{
				HttpWebResponse response = (HttpWebResponse)request.GetResponse();
				StreamReader reader = new StreamReader(response.GetResponseStream());
				if (response.Cookies.Count > 0) Cookies = response.Cookies;
				string resultStr = reader.ReadToEnd();
				result = new ReturnSet( true, resultStr );
			}catch (WebException ex){
				result = new ReturnSet( false, ex, "ERROR_MESSAGE=HttpWebResponse error, can not connect to server ("+request.Address.OriginalString+")\r\n\r\n" + ex.ToString() );
			}
			return result;
		}
		
		private static ReturnSet PostParameter(HttpWebRequest request, Hashtable paramList){
			return PostParameter( request, paramList, new Hashtable() );
		}
		
		private static ReturnSet PostParameter(HttpWebRequest request, Hashtable paramList, Hashtable fileList){
			
			ReturnSet result = new ReturnSet(false);
			try{
				//init list of mimePart
				List<MimePart> mimeParts = new List<MimePart>();
				
				//add parameters
				foreach( string param in paramList.Keys ){
					mimeParts.Add( new MimePart( param, paramList[param].ToString() ) );
				}
				
				//add files
				foreach( string fieldname in fileList.Keys ){
					mimeParts.Add( new MimePart( fieldname, fileList[fieldname].ToString(), null ) );
				}
				
				//init boundary, footer and content length
				long contentLength = 0;
				string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
				request.ContentType = "multipart/form-data; boundary=" + boundary;
				request.Method = "POST";
				
				byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");
				
				foreach (MimePart part in mimeParts)
				{
					contentLength += part.GenerateHeaderFooterData(boundary);
				}
				
				request.ContentLength = contentLength + _footer.Length;
				
				//write parameter and file to stream -> send to server
				byte[] buffer = new byte[8192];
				byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
				int read;
				
				using (Stream s = request.GetRequestStream())
				{
					foreach (MimePart part in mimeParts)
					{
						s.Write(part.Header, 0, part.Header.Length);
						
						while ((read = part.Data.Read(buffer, 0, buffer.Length)) > 0)
							s.Write(buffer, 0, read);
						
						part.Data.Dispose();
						
						s.Write(afterFile, 0, afterFile.Length);
					}
					
					s.Write(_footer, 0, _footer.Length);
				}
				result = new ReturnSet( true );
			}catch (Exception ex){
				result = new ReturnSet( false, ex, "ERROR_MESSAGE=Post parameter error, can not connect to server ("+request.Address.OriginalString+")\r\n\r\n" + ex.ToString());
			}
			return result;
		}
		#endregion
		
		
		
		#region Connect(string url, string paramList)
		
		
		/// <summary>
		/// Connect and send param to server, get response web http
		/// </summary>
		/// <param name="url">URL connect to server</param>
		/// <param name="paramList">Hashtable with key is param name and value is param value</param>
		/// <param name="certificateFile">Certificate file path, null if not available</param>
		/// <param name="retryTimes">Total times you want to retry if execute function fail</param>
		/// <returns>
		///  - Sucess:true, Result: response http string
		///  - Sucess:false, Result: Exception object, Message: error message
		/// </returns>
		public static ReturnSet connectHttp(string url, Hashtable paramList, NetworkSecurity netsec, int retryTimes){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = connectHttp( url, paramList, netsec );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}


		public static ReturnSet connectHttp(string url, Hashtable paramList)
		{
			return connectHttp(url, paramList, null);
		}
		/// <summary>
		/// Connect and send param to server, get response web http
		/// </summary>
		/// <param name="url">URL connect to server</param>
		/// <param name="paramList">Hashtable with key is param name and value is param value</param>
		/// <param name="certificateFile">Certificate file path, null if not available</param>
		/// <returns>
		///  - Sucess:true, Result: response http string
		///  - Sucess:false, Result: Exception object, Message: error message
		/// </returns>
		public static ReturnSet connectHttp(string url, Hashtable paramList, NetworkSecurity netsec)
		{
			HttpWebRequest request = null;
			ReturnSet result = new ReturnSet( false, null );
			try{
				ReturnSet requestResult = InitWebRequest(url, netsec);
				if(requestResult.Success) request = (HttpWebRequest)requestResult.Result;
				else return result;
				
				result = PostParameter( request, paramList);
				
				if( result.Success )
					result = GetResponseStr( request );
				
			}catch(Exception ex){
				result = new ReturnSet( false, ex, "ERROR_MESSAGE=Can not [connect] to url("+url+").\r\n\r\n" + ex.ToString() );
			}
			return result;
		}
		#endregion
		
		#region UploadFile(string file, string url)
		public delegate ReturnSet uploadFileHttpDelegate(Hashtable fileList, string url, Hashtable paramList, NetworkSecurity netsec);
		
		/// <summary>
		/// Upload file to HTTPS server using POST method
		/// </summary>
		/// <param name="fileList">Hashtable to upload one or mult-files. key is fieldname, value is file path. Ex: file,C:\abc.exe</param>
		/// <param name="url">URL connect to server</param>
		/// <param name="paramList">Hashtable with key is param name and value is param value</param>
		/// <param name="certificateFile">Certificate file path, null if not available</param>
		/// <param name="retryTimes">Total times you want to retry if execute function fail</param>
		/// <returns>
		///  - Sucess:true, Result: response http string
		///  - Sucess:false, Result: Exception object, Message: error message
		/// </returns>
		public static ReturnSet UploadFileHttp(Hashtable fileList, string url, Hashtable paramList, NetworkSecurity netsec, int retryTimes){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = UploadFileHttp(fileList, url, paramList, netsec );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}

		public static ReturnSet UploadFileHttp(Hashtable fileList, string url, Hashtable paramList)
		{
			return UploadFileHttp(fileList, url, paramList, null);
		}
		/// <summary>
		/// Upload file to HTTPS server using POST method
		/// </summary>
		/// <param name="fileList">Hashtable to upload one or mult-files. key is fieldname, value is file path. Ex: file,C:\abc.exe</param>
		/// <param name="url">URL connect to server</param>
		/// <param name="paramList">Hashtable with key is param name and value is param value</param>
		/// <param name="certificateFile">Certificate file path, null if not available</param>
		/// <returns>
		///  - Sucess:true, Result: response http string
		///  - Sucess:false, Result: Exception object, Message: error message
		/// </returns>
		public static ReturnSet UploadFileHttp(Hashtable fileList, string url, Hashtable paramList, NetworkSecurity netsec)
		{
			HttpWebRequest request = null;
			
			ReturnSet requestResult = InitWebRequest(url, netsec);
			if(requestResult.Success) request = (HttpWebRequest)requestResult.Result;
			else return requestResult;
			
			ReturnSet result = PostParameter( request, paramList, fileList );
			
			if( result.Success )
				result = GetResponseStr(request);
			
			return result;
		}
		#endregion
		
		#region DownloadFile( string url, string destFile )
		public delegate ReturnSet DownloadFileHttpDelegate( string url, string destFile, Hashtable paramList, string certificateFile );
		
		/// <summary>
		/// Download file and save to destination file
		/// </summary>
		/// <param name="url">URL of file in server</param>
		/// <param name="destFile">file path whese to save downloaded file</param>
		/// <param name="paramList">Hashtable with key is param name and value is param value</param>
		/// <param name="certificateFile">Certificate file path, null if not available</param>
		/// <param name="retryTimes">Total times you want to retry if execute function fail</param>
		/// <returns>
		///  - Sucess:true, Result: null
		///  - Sucess:false, Result: Exception object, Message: error message
		/// </returns>
		public static ReturnSet downloadFileHttp( string url, string destFile, Hashtable paramList,NetworkSecurity netsec, int retryTimes){
			int time = 0;
			ReturnSet result = null;
			
			while( time < retryTimes ){
				result = downloadFileHttp( url, destFile, paramList, netsec );
				time++;
				if(result.Success) break;
			}
			
			return result;
		}

		public static ReturnSet downloadFileHttp(string url, string destFile, Hashtable paramList)
		{
			return downloadFileHttp(url, destFile, paramList, null);
		}

		/// <summary>
		/// Download file and save to destination file
		/// </summary>
		/// <param name="url">URL of file in server</param>
		/// <param name="destFile">file path whese to save downloaded file</param>
		/// <param name="paramList">Hashtable with key is param name and value is param value</param>
		/// <param name="certificateFile">Certificate file path, null if not available</param>
		/// <param name="retryTimes">Total times you want to retry if execute function fail</param>
		/// <returns>
		///  - Sucess:true, Result: null
		///  - Sucess:false, Result: Exception object, Message: error message
		/// </returns>
		public static ReturnSet downloadFileHttp(string url, string destFile, Hashtable paramList, NetworkSecurity netsec)
		{
			Stream readStream 		= null;
			FileStream writeStream 	= null;
			HttpWebRequest request 	= null;
			
			ReturnSet requestResult = InitWebRequest(url, netsec);
			if(requestResult.Success) request = (HttpWebRequest)requestResult.Result;
			else return requestResult;
			
			//post param
			ReturnSet result = PostParameter( request, paramList );
			if( !result.Success ) return result;
			
			try{
				HttpWebResponse response = (HttpWebResponse)request.GetResponse();
				readStream = response.GetResponseStream();
				writeStream = new FileStream(destFile, FileMode.Create, FileAccess.Write);
				
				int Length = 256;
				Byte [] buffer = new Byte[Length];
				int bytesRead = readStream.Read(buffer,0,Length);
				// write the required bytes
				while( bytesRead > 0 )
				{
					writeStream.Write(buffer,0,bytesRead);
					bytesRead = readStream.Read(buffer,0,Length);
				}
				result = new ReturnSet(true, null );
			}catch(Exception ex){
				result = new ReturnSet( false, ex, "ERROR_MESSAGE=Can not [download file] (url: "+url+").Error::\r\n\r\n" + ex.ToString() );
			}finally{
				if(readStream!=null) readStream.Close();
				if(writeStream!=null) writeStream.Close();
			}
			return result;
		}
		#endregion
		
		public static bool isConnectedToInternet(){
			int desc;
			return APILib.InternetGetConnectedState( out desc, 0 );
		}
	}
	
	#region Classes
	public class MimePart
	{
		Hashtable _headers = new Hashtable();
		byte[] _header;
		Stream _data;
		
		public enum ContentType{
			application_octetstream = 0
		}
		
		public MimePart(string parameterName, string value){
			_headers["Content-Disposition"] = "form-data; name=\"" + parameterName + "\"";
			_data = new MemoryStream(Encoding.UTF8.GetBytes(value));
		}
		
		public MimePart(string fieldName, string fileName, string contentType){
			_headers["Content-Disposition"] = "form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"";
			_headers["Content-Type"] = StringLib.isValid(contentType) ? contentType : "application/octet-stream";
			_data = File.OpenRead(fileName);
		}

		public Hashtable Headers
		{
			get { return _headers; }
		}

		public byte[] Header
		{
			get { return _header; }
		}

		public long GenerateHeaderFooterData(string boundary)
		{
			StringBuilder sb = new StringBuilder();

			sb.Append("--");
			sb.Append(boundary);
			sb.AppendLine();
			foreach (string key in _headers.Keys)
			{
				sb.Append(key);
				sb.Append(": ");
				sb.AppendLine(_headers[key].ToString());
			}
			sb.AppendLine();

			_header = Encoding.UTF8.GetBytes(sb.ToString());

			return _header.Length + Data.Length + 2;
		}

		public Stream Data {
			get
			{
				return _data;
			}
		}
	}
	
	public class NetworkSecurity{
		
		
		private WebProxy proxy;
		public WebProxy Proxy{
			get{
				return proxy;
			}
		}
		
		private X509Certificate cert;
		public X509Certificate Certificate{
			get{
				return cert;
			}
		}
		public NetworkSecurity(){}
		
		public NetworkSecurity(string certificateFile){
			if( File.Exists(certificateFile) ){
				cert = X509Certificate.CreateFromCertFile( certificateFile );
			}
		}
		
		public NetworkSecurity(string host, int port, string domain, string username, string password){
			proxy = new WebProxy(host, port);
			proxy.Credentials = new NetworkCredential(username, password, domain);
		}
		
		public NetworkSecurity(string host, int port){
			proxy = new WebProxy(host, port);
			proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
		}
	}
	#endregion
}
